package com.hw.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;

/**
 * NIO服务端：selector 多路复用模式
 * selector多路复用模式下必须设置channel为非阻塞
 * @program: NIO_Netty
 * @author: hewei
 * @date: 2023-03-31 16:48
 **/
public class SelectChannelServer {
    /*多客户端连接时存在线程安全问题，需要优化，建议每个连接给一个ByteBuffer，而不是所有连接共用一个*/
    private static ByteBuffer buffer=ByteBuffer.allocateDirect(1024);
    private static Charset charset = Charset.forName("UTF-8");

    public static void main(String[] args) throws IOException, InterruptedException {
        try (ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()){
            serverSocketChannel.bind(new InetSocketAddress(9696));
            serverSocketChannel.configureBlocking(false);
            Selector selector = Selector.open();
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            while (selector.select() > 0) {
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    executeEvent(selector, iterator.next(),Duration.ofSeconds(2));
                    iterator.remove();
                }
            }
        }
    }

    /*执行 selector 中的事件*/
    private static void executeEvent(Selector selector, SelectionKey key, Duration duration) throws IOException, InterruptedException {
        Long timestamp=System.currentTimeMillis();
        if (key.isAcceptable()){
            ServerSocketChannel serverSocketChannel= (ServerSocketChannel) key.channel();
            SocketChannel socketChannel=serverSocketChannel.accept();
            socketChannel.configureBlocking(false);
            System.out.println("Server accepted connection - "+timestamp+":"+socketChannel.getRemoteAddress());
            socketChannel.register(selector, SelectionKey.OP_WRITE);
        }else if (key.isReadable()){
            SocketChannel socketChannel= (SocketChannel) key.channel();
            socketChannel.read(buffer);
            buffer.flip();
            System.out.println("Server received："+socketChannel.getRemoteAddress()+"["+charset.decode(buffer)+"]");
            buffer.clear();
            socketChannel.register(selector, SelectionKey.OP_WRITE);
        }else if (key.isWritable()){
            SocketChannel socketChannel= (SocketChannel) key.channel();
            buffer.put(("你好，这是服务端:"+timestamp).getBytes(StandardCharsets.UTF_8));
            buffer.flip();
            socketChannel.write(buffer);
            System.out.println("Server send：你好，这是服务端！");
            buffer.clear();
            socketChannel.register(selector,SelectionKey.OP_READ);
        }
        TimeUnit.SECONDS.sleep(duration.getSeconds());
    }
}